const main = {
  /**
   * 渲染块
   * @param {Object} params
   */
  drawBlock({
    text,
    width = 0,
    height,
    x,
    y,
    paddingLeft = 0,
    paddingRight = 0,
    borderWidth,
    backgroundColor,
    borderColor,
    borderRadius = 0,
    opacity = 1,
  }) {
    // 判断是否块内有文字
    let blockWidth = 0; // 块的宽度
    let textX = 0;
    let textY = 0;
    if (typeof text !== "undefined") {
      // 如果有文字并且块的宽度小于文字宽度，块的宽度为 文字的宽度 + 内边距
      const textWidth = this._getTextWidth(typeof text.text === "string" ? text : text.text);
      blockWidth = textWidth > width ? textWidth : width;
      blockWidth += paddingLeft + paddingLeft;

      const {
        textAlign = "left", text: textCon
      } = text;
      textY = height / 2 + y; // 文字的y轴坐标在块中线
      if (textAlign === "left") {
        // 如果是右对齐，那x轴在块的最左边
        textX = x + paddingLeft;
      } else if (textAlign === "center") {
        textX = blockWidth / 2 + x;
      } else {
        textX = x + blockWidth - paddingRight;
      }
    } else {
      blockWidth = width;
    }

    if (backgroundColor) {
      // 画面
      this.ctx.save();
      this.ctx.globalAlpha = opacity;
      this.ctx.fillStyle = backgroundColor;
      if (borderRadius > 0) {
        // 画圆角矩形
        this._drawRadiusRect(x, y, blockWidth, height, borderRadius);
        this.ctx.fill();
      } else {
        this.ctx.fillRect(this.toPx(x), this.toPx(y), this.toPx(blockWidth), this.toPx(height));
      }
      this.ctx.restore();
    }
    if (borderWidth) {
      // 画线
      this.ctx.save();
      this.ctx.globalAlpha = opacity;
      this.ctx.strokeStyle = borderColor;
      this.ctx.lineWidth = this.toPx(borderWidth);
      if (borderRadius > 0) {
        // 画圆角矩形边框
        this._drawRadiusRect(x, y, blockWidth, height, borderRadius);
        this.ctx.stroke();
      } else {
        this.ctx.strokeRect(this.toPx(x), this.toPx(y), this.toPx(blockWidth), this.toPx(height));
      }
      this.ctx.restore();
    }

    if (text) {
      this.drawText(Object.assign(text, {
        x: textX,
        y: textY
      }));
    }
  },

  /**
   * 渲染文字
   * @param {Object} params
   */
  drawText(params) {
    const {
      x,
      y,
      fontSize,
      color,
      baseLine,
      textAlign,
      text,
      opacity = 1,
      width,
      lineNum,
      lineHeight
    } = params;
    if (Object.prototype.toString.call(text) === "[object Array]") {
      let preText = {
        x,
        y,
        baseLine
      };
      text.forEach((item) => {
        preText.x += this.toPx(item.marginLeft || 0);
        const textWidth = this._drawSingleText(
          Object.assign(item, {
            ...preText,
          })
        );
        preText.x += textWidth + this.toPx(item.marginRight || 0); // 下一段字的x轴为上一段字x + 上一段字宽度
      });
    } else {
      this._drawSingleText(params);
    }
  },

  /**
   * 渲染图片
   * @param {Object} params
   */
  drawImage({
    imgPath,
    x,
    y,
    w,
    h,
    sx,
    sy,
    sw,
    sh,
    borderRadius = 0,
    borderWidth = 0,
    borderColor
  }) {
    return new Promise((resolve) => {
      const img = this.node.createImage();
      img.onload = () => {
        this.ctx.save();
        if (borderRadius > 0) {
          this._drawRadiusRect(x, y, w, h, borderRadius);
          this.ctx.strokeStyle = "rgba(255,255,255,0)";
          this.ctx.stroke();
          this.ctx.clip();
          this.ctx.drawImage(
            img,
            this.toPx(sx),
            this.toPx(sy),
            this.toPx(sw),
            this.toPx(sh),
            this.toPx(x),
            this.toPx(y),
            this.toPx(w),
            this.toPx(h)
          );
          if (borderWidth > 0) {
            this.ctx.strokeStyle = borderColor;
            this.ctx.lineWidth = this.toPx(borderWidth);
            this.ctx.stroke();
          }
        } else {
          this.ctx.drawImage(
            img,
            this.toPx(sx),
            this.toPx(sy),
            this.toPx(sw),
            this.toPx(sh),
            this.toPx(x),
            this.toPx(y),
            this.toPx(w),
            this.toPx(h)
          );
        }
        this.ctx.restore();
        resolve();
      };
      img.src = imgPath;
    });
  },
  /**
   * 渲染线
   * @param {Object} param
   */
  drawLine({
    startX,
    startY,
    endX,
    endY,
    color,
    width
  }) {
    this.ctx.save();
    this.ctx.beginPath();
    this.ctx.strokeStyle = color;
    this.ctx.lineWidth = this.toPx(width);
    this.ctx.moveTo(this.toPx(startX), this.toPx(startY));
    this.ctx.lineTo(this.toPx(endX), this.toPx(endY));
    this.ctx.stroke();
    this.ctx.closePath();
    this.ctx.restore();
  },
  downloadResource({
    images = []
  }) {
    const drawList = [];
    images.forEach((image, index) => drawList.push(this._downloadImageAndInfo(image, index)));
    return Promise.all(drawList);
  },
  initCanvas(w, h, debug) {
    const {
      platform
    } = wx.getSystemInfoSync();

    return new Promise((resolve) => {

      if (platform === "ios") {
        this.setData({
          pxWidth: this.toPx(w),
          pxHeight: this.toPx(h),
          debug,
        });
        // ios系统动态设置canvas宽高后立即绘制canvas会偶现变形的BUG
        setTimeout(() => {
          resolve();
        }, 100);
      } else {
        this.setData({
            pxWidth: this.toPx(w),
            pxHeight: this.toPx(h),
            debug,
          },
          resolve
        );
      }
    });
  },
};
const handle = {
  /**
   * 画圆角矩形
   */
  _drawRadiusRect(x, y, w, h, r) {
    const br = r / 2;
    this.ctx.beginPath();
    this.ctx.moveTo(this.toPx(x + br), this.toPx(y)); // 移动到左上角的点
    this.ctx.lineTo(this.toPx(x + w - br), this.toPx(y));
    this.ctx.arc(
      this.toPx(x + w - br),
      this.toPx(y + br),
      this.toPx(br),
      2 * Math.PI * (3 / 4),
      2 * Math.PI * (4 / 4)
    );
    this.ctx.lineTo(this.toPx(x + w), this.toPx(y + h - br));
    this.ctx.arc(this.toPx(x + w - br), this.toPx(y + h - br), this.toPx(br), 0, 2 * Math.PI * (1 / 4));
    this.ctx.lineTo(this.toPx(x + br), this.toPx(y + h));
    this.ctx.arc(
      this.toPx(x + br),
      this.toPx(y + h - br),
      this.toPx(br),
      2 * Math.PI * (1 / 4),
      2 * Math.PI * (2 / 4)
    );
    this.ctx.lineTo(this.toPx(x), this.toPx(y + br));
    this.ctx.arc(this.toPx(x + br), this.toPx(y + br), this.toPx(br), 2 * Math.PI * (2 / 4), 2 * Math.PI * (3 / 4));
  },
  /**
   * 计算文本长度
   * @param {Array|Object}} text 数组 或者 对象
   */
  _getTextWidth(text) {
    let texts = [];
    if (Object.prototype.toString.call(text) === "[object Object]") {
      texts.push(text);
    } else {
      texts = text;
    }
    let width = 0;
    texts.forEach(({
      fontSize,
      text,
      marginLeft = 0,
      marginRight = 0
    }) => {
      this.ctx.fontSize = this.toPx(fontSize);
      width += this.ctx.measureText(text).width + marginLeft + marginRight;
    });

    return this.toRpx(width);
  },
  /**
   * 渲染一段文字
   */
  _drawSingleText({
    x,
    y,
    fontSize,
    color,
    baseLine,
    textAlign = "left",
    text,
    opacity = 1,
    textDecoration = "none",
    width,
    lineNum = 1,
    lineHeight = 0,
    fontWeight = "normal",
    fontStyle = "normal",
    fontFamily = "sans-serif",
  }) {
    this.ctx.save();
    this.ctx.beginPath();
    this.ctx.font = fontStyle + " " + fontWeight + " " + this.toPx(fontSize, true) + "px " + fontFamily;
    this.ctx.globalAlpha = opacity;
    this.ctx.fillStyle = color;
    this.ctx.textBaseline = baseLine;
    this.ctx.textAlign = textAlign;
    let textWidth = this.toRpx(this.ctx.measureText(text).width);
    const textArr = [];
    if (textWidth > width) {
      // 文本宽度 大于 渲染宽度
      let fillText = "";
      let line = 1;
      for (let i = 0; i <= text.length - 1; i++) {
        // 将文字转为数组，一行文字一个元素
        fillText = fillText + text[i];
        if (this.toRpx(this.ctx.measureText(fillText).width) >= width) {
          if (line === lineNum) {
            if (i !== text.length - 1) {
              fillText = fillText.substring(0, fillText.length - 1) + "...";
            }
          }
          if (line <= lineNum) {
            textArr.push(fillText);
          }
          fillText = "";
          line++;
        } else {
          if (line <= lineNum) {
            if (i === text.length - 1) {
              textArr.push(fillText);
            }
          }
        }
      }
      textWidth = width;
    } else {
      textArr.push(text);
    }

    textArr.forEach((item, index) => {
      this.ctx.fillText(item, this.toPx(x), this.toPx(y + (lineHeight || fontSize) * index));
    });

    this.ctx.restore();

    // textDecoration
    if (textDecoration !== "none") {
      let lineY = y;
      if (textDecoration === "line-through") {
        // 目前只支持贯穿线
        lineY = y;

        // 小程序画布baseLine偏移阈值
        let threshold = 5;

        // 根据baseLine的不同对贯穿线的Y坐标做相应调整
        switch (baseLine) {
          case "top":
            lineY += fontSize / 2 + threshold;
            break;
          case "middle":
            break;
          case "bottom":
            lineY -= fontSize / 2 + threshold;
            break;
          default:
            lineY -= fontSize / 2 - threshold;
            break;
        }
      }
      this.ctx.save();
      this.ctx.moveTo(this.toPx(x), this.toPx(lineY));
      this.ctx.lineTo(this.toPx(x) + this.toPx(textWidth), this.toPx(lineY));
      this.ctx.strokeStyle = color;
      this.ctx.stroke();
      this.ctx.restore();
    }

    return textWidth;
  },
};
const helper = {
  /**
   * 下载图片并获取图片信息
   */
  _downloadImageAndInfo(image, index) {
    return new Promise((resolve, reject) => {
      const {
        x,
        y,
        url,
        zIndex
      } = image;
      const imageUrl = url;
      // 下载图片
      this._downImage(imageUrl, index)
        // 获取图片信息
        .then((imgPath) => this._getImageInfo(imgPath, index))
        .then(({
          imgPath,
          imgInfo
        }) => {
          // 根据画布的宽高计算出图片绘制的大小，这里会保证图片绘制不变形
          let sx;
          let sy;
          const borderRadius = image.borderRadius || 0;
          const setWidth = image.width;
          const setHeight = image.height;
          const width = this.toRpx(imgInfo.width);
          const height = this.toRpx(imgInfo.height);

          if (width / height <= setWidth / setHeight) {
            sx = 0;
            sy = (height - (width / setWidth) * setHeight) / 2;
          } else {
            sy = 0;
            sx = (width - (height / setHeight) * setWidth) / 2;
          }
          if (!this.drawArr) this.drawArr = [];
          this.drawArr.push({
            type: "image",
            borderRadius,
            borderWidth: image.borderWidth,
            borderColor: image.borderColor,
            zIndex: typeof zIndex !== "undefined" ? zIndex : index,
            imgPath,
            sx,
            sy,
            sw: width - sx * 2,
            sh: height - sy * 2,
            x,
            y,
            w: setWidth,
            h: setHeight,
          });
          resolve();
        })
        .catch((err) => reject(err));
    });
  },
  /**
   * 下载图片资源
   * @param {*} imageUrl
   */
  _downImage(imageUrl) {
    return new Promise((resolve, reject) => {
      if (/^http(s)?:\/\/tmp\//.test(imageUrl)) {
        //剔除掉http://tmp/路径以支持微信临时文件;
        resolve(imageUrl);
      } else if (/^http/.test(imageUrl) && !new RegExp(wx.env.USER_DATA_PATH).test(imageUrl)) {
        wx.downloadFile({
          url: this._mapHttpToHttps(imageUrl),
          success: (res) => {
            if (res.statusCode === 200) {
              resolve(res.tempFilePath);
            } else {
              reject(res.errMsg);
            }
          },
          fail(err) {
            reject(err);
          },
        });
      } else {
        // 支持本地地址
        resolve(imageUrl);
      }
    });
  },
  /**
   * 获取图片信息
   * @param {*} imgPath
   * @param {*} index
   */
  _getImageInfo(imgPath, index) {
    return new Promise((resolve, reject) => {
      wx.getImageInfo({
        src: imgPath,
        success(res) {
          resolve({
            imgPath,
            imgInfo: res,
            index
          });
        },
        fail(err) {
          reject(err);
        },
      });
    });
  },
  toPx(rpx, int) {
    if (int) {
      return parseInt(rpx * this.factor * this.pixelRatio);
    }
    return rpx * this.factor * this.pixelRatio;
  },
  toRpx(px, int) {
    if (int) {
      return parseInt(px / (this.factor * this.pixelRatio));
    }
    return px / (this.factor * this.pixelRatio);
  },
  /**
   * 将http转为https
   * @param {String}} rawUrl 图片资源url
   */
  _mapHttpToHttps(rawUrl) {
    if (rawUrl.indexOf(":") < 0) {
      return rawUrl;
    }
    const urlComponent = rawUrl.split(":");
    if (urlComponent.length === 2) {
      if (urlComponent[0] === "http") {
        urlComponent[0] = "https";
        return `${urlComponent[0]}:${urlComponent[1]}`;
      }
    }
    return rawUrl;
  },
};
Component({
  properties: {},
  created() {
    const sysInfo = wx.getSystemInfoSync();
    const {
      pixelRatio,
      screenWidth
    } = sysInfo;
    this.factor = screenWidth / 750;
    this.pixelRatio = pixelRatio;
  },
  methods: Object.assign({
      /**
       * 计算画布的高度
       * @param {*} config
       */
      getHeight(config) {
        const getTextHeight = (text) => {
          let fontHeight = text.lineHeight || text.fontSize;
          let height = 0;
          if (text.baseLine === "top") {
            height = fontHeight;
          } else if (text.baseLine === "middle") {
            height = fontHeight / 2;
          } else {
            height = 0;
          }
          return height;
        };
        const heightArr = [];
        (config.blocks || []).forEach((item) => {
          heightArr.push(item.y + item.height);
        });
        (config.texts || []).forEach((item) => {
          let height;
          if (Object.prototype.toString.call(item.text) === "[object Array]") {
            item.text.forEach((i) => {
              height = getTextHeight({
                ...i,
                baseLine: item.baseLine,
              });
              heightArr.push(item.y + height);
            });
          } else {
            height = getTextHeight(item);
            heightArr.push(item.y + height);
          }
        });
        (config.images || []).forEach((item) => {
          heightArr.push(item.y + item.height);
        });
        (config.lines || []).forEach((item) => {
          heightArr.push(item.startY);
          heightArr.push(item.endY);
        });
        const sortRes = heightArr.sort((a, b) => b - a);
        let canvasHeight = 0;
        if (sortRes.length > 0) {
          canvasHeight = sortRes[0];
        }
        if (config.height < canvasHeight || !config.height) {
          return canvasHeight;
        } else {
          return config.height;
        }
      },
      async create(config) {
        await this.initCtx();
        this.pixelRatio = config.pixelRatio || this.pixelRatio;
        const height = this.getHeight(config);
        let height_rate = 1.0
        let pixelRatioReal = this.pixelRatio
        this.initCanvas(config.width, height, config.debug)
          .then(async () => {
            this.node.width = this.data.pxWidth * this.pixelRatio;
            this.node.height = this.data.pxHeight * this.pixelRatio;

            if (this.node.height > 1920) {
              height_rate = this.node.height / 1920
              pixelRatioReal = pixelRatioReal / height_rate
              this.node.height = 1920
              this.node.width = this.node.width / height_rate
            }

            this.ctx.scale(this.pixelRatio, this.pixelRatio);

            // 设置画布底色
            if (config.backgroundColor) {
              this.ctx.save();
              this.ctx.fillStyle = config.backgroundColor;
              this.ctx.fillRect(0, 0, this.toPx(config.width), this.toPx(height));
              this.ctx.restore();
            }
            const {
              texts = [], blocks = [], lines = []
            } = config;
            if (!this.drawArr) this.drawArr = [];
            const queue = this.drawArr
              .concat(
                texts.map((item) => {
                  item.type = "text";
                  item.zIndex = item.zIndex || 0;
                  return item;
                })
              )
              .concat(
                blocks.map((item) => {
                  item.type = "block";
                  item.zIndex = item.zIndex || 0;
                  return item;
                })
              )
              .concat(
                lines.map((item) => {
                  item.type = "line";
                  item.zIndex = item.zIndex || 0;
                  return item;
                })
              );
            // 按照顺序排序
            queue.sort((a, b) => a.zIndex - b.zIndex);

            for (let i = 0, len = queue.length; i < len; i++) {
              let item = queue[i];
              item.y = item.y / height_rate
              item.x = item.x / height_rate
              item.fontSize = item.fontSize / height_rate
              if (item.type === "image") {
                item.h = item.h / height_rate
                item.w = item.w / height_rate
                item.borderRadius = item.borderRadius / height_rate
                await this.drawImage(item);
              } else if (item.type === "text") {
                item.lineHeight = item.lineHeight / height_rate
                item.marginLeft = item.marginLeft / height_rate
                item.width = item.width / height_rate
                this.drawText(item);
              } else if (item.type === "block") {
                item.height = item.height / height_rate
                item.width = item.width / height_rate
                this.drawBlock(item);
              } else if (item.type === "line") {
                this.drawLine(item);
              }
            }

            wx.canvasToTempFilePath({
                canvas: this.node,
                success: (res) => {
                  this.triggerEvent("success", res.tempFilePath);
                },
                fail: (err) => {
                  this.triggerEvent("fail", err);
                },
              },
              this
            );
          })
          .catch((err) => {
            wx.showToast({
              icon: "none",
              title: err.errMsg || "生成失败",
            });
            console.error(err);
          });
      },
      initCtx() {
        return new Promise((resolve) => {
          wx.createSelectorQuery()
            .in(this)
            .select("#canvasid")
            .fields({
              node: true,
            })
            .exec((res) => {
              this.node = res[0].node;
              this.ctx = this.node.getContext("2d");
              resolve();
            });
        });
      },
    },
    main,
    handle,
    helper
  ),
});